package com.sky.service.impl;

import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.sky.constant.ArticleConstant;
import com.sky.context.BaseContext;
import com.sky.dto.ArticleDTO;
import com.sky.dto.ArticlePageDTO;
import com.sky.dto.ArticleStatus;
import com.sky.dto.ArticleUpdateDTO;
import com.sky.entity.Article;
import com.sky.entity.Reply;
import com.sky.exception.ArticleException;
import com.sky.mapper.*;
import com.sky.result.PageResult;
import com.sky.service.ArticleService;
import com.sky.service.CommentService;
import com.sky.service.ReplyService;
import com.sky.vo.ArticleUpdateVO;
import com.sky.vo.ArticleVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;

@Slf4j
@Service
public class ArticleServiceImpl implements ArticleService {

    private final RedisTemplate<String,Object> redisTemplate;

    private final ArticleMapper articleMapper;


    private final ArticleTagMapper articleTagMapper;

    private final CommentMapper commentMapper;

    private final ReplyMapper replyMapper;

    @Autowired
    public ArticleServiceImpl(ArticleMapper articleMapper,
                              ArticleTagMapper articleTagMapper,
                              RedisTemplate<String, Object> redisTemplate,
                              CommentMapper commentMapper,
                              ReplyMapper replyMapper) {
        this.articleMapper = articleMapper;
        this.articleTagMapper = articleTagMapper;
        this.redisTemplate = redisTemplate;
        this.commentMapper = commentMapper;
        this.replyMapper = replyMapper;
    }


    /**
     * 新增文章
     */
    @Override
    @Transactional
    @PreAuthorize("@rootPermission.hasRoot(1L)")
    public void add(ArticleDTO articleDTO) {

        log.info("尝试获取当前用户ID: {}", BaseContext.getCurrentId());

        Article article = Article.builder()
                .title(articleDTO.getTitle())
                .content(articleDTO.getContent())
                //ThreadLocal获取当前登录用户的id
                .userId(BaseContext.getCurrentId())

                .viewCount(ArticleConstant.DEFAULT_LONG)
                .likeCount(ArticleConstant.DEFAULT_LONG)
                .commentCount(ArticleConstant.DEFAULT_LONG)
                .isTop(ArticleConstant.DEFAULT_INTEGER)
                .isRecommend(ArticleConstant.DEFAULT_INTEGER)
                .categoryId(articleDTO.getCategoryId())
                .build();

        //插入数据库
        articleMapper.insertArticle(article);


        //获取文章id
        Long articleId = article.getId();
        //获取列表名
        List<Long> tagIds = articleDTO.getTagIdList();
        if(tagIds != null && !tagIds.isEmpty()){
            //批量存入文章标签关联表
            articleTagMapper.batchInsertArticleTag(articleId,tagIds);
            }

        //将文章浏览数存入Redis中
        String key = ArticleConstant.ARTICLE_VIEW + article.getId();
        Long viewCount = 0L;
        redisTemplate.opsForValue().set(key, viewCount);
        }


    /**
     * 分页查询文章
     */
    @Override
    public PageResult page(ArticlePageDTO articlePageDTO) {
        PageHelper.startPage(articlePageDTO.getPage(),articlePageDTO.getPageSize());

        Page<ArticleVO> page = articleMapper.pageQuery(articlePageDTO);
        return new PageResult(page.getTotal(),page.getResult());

    }

    /**
     * 删除文章
     */
    @Override
    @Transactional
    @PreAuthorize("@rootPermission.hasRoot(1L)")
    public void delete(Long id) {

        //删除存入Redis中的浏览数
        String key = ArticleConstant.ARTICLE_VIEW + id;
        redisTemplate.delete(key);

        //删除文章标签关联表
        articleTagMapper.deleteByArticleId(id);

        //根据文章id查找对应的评论id
        List<Long> commentIds = commentMapper.getCommentIdsByArticleId(id);
        if(commentIds!= null &&!commentIds.isEmpty()){

            //根据评论id查找对应的回复id
            List<Long> replyIds = replyMapper.getReplyIdsByCommentIds(commentIds);

            if (replyIds != null && !replyIds.isEmpty()) {
                //根据回复id删除回复
                replyMapper.deleteReplyByReplyIds(replyIds);
            }
        }

        //根据文章id删除评论
        commentMapper.deleteCommentByArticleId(id);

        //删除文章表
        articleMapper.deleteById(id);
    }

    /**
     * 修改文章
     */
    @Override
    @PreAuthorize("@rootPermission.hasRoot(1L)")
    public void updateArticle(ArticleUpdateDTO articleUpdateDTO) {

        //修改文章表
        articleMapper.updateArticle(articleUpdateDTO);

        //获取文章id
        Long articleId = articleUpdateDTO.getId();

        //根据文章id删除文章标签关联表
        articleTagMapper.deleteByArticleId(articleId);

        //批量插入文章标签关联表
        List<Long> tagIds = articleUpdateDTO.getTagIdList();
        if(tagIds!= null &&!tagIds.isEmpty()){

            //批量插入文章标签关联表
            articleTagMapper.batchInsertArticleTag(articleId,tagIds);
        }

    }

    /**
     * 根据id查询文章
     */
    @Override
    public ArticleUpdateVO getById(Long id) {

        ArticleUpdateVO articleUpdateVO = new ArticleUpdateVO();

        //根据id查询文章
        Article article = articleMapper.getById(id);

        if(article == null){
            throw new ArticleException(ArticleConstant.ARTICLE_NOT_FOUND);
        }

        BeanUtils.copyProperties(article,articleUpdateVO);

        //根据id查询文章标签关联表
        List<Long> tagIds = articleTagMapper.getTagIdsByArticleId(id);
        articleUpdateVO.setTagIdList(tagIds);


        return articleUpdateVO;

    }

    /**
     * 修改文章状态
     */
    @Override
    @PreAuthorize("@rootPermission.hasRoot(1L)")
    public void changeStatus(ArticleUpdateDTO articleUpdateDTO) {
        articleMapper.updateArticle(articleUpdateDTO);
    }

    /**
     * 统计文章数量
     */
    @Override
    public Long countArticle() {
        return articleMapper.countArticle();
    }

    /**
     * 根据分类id查询改分类下所有的文章
     * @param categoryId
     * @return
     */
    @Override
    public List<ArticleVO> list(Long categoryId) {
        return articleMapper.list(categoryId);
    }



    /**
     * 点赞文章
     */
    @Override
    public void like(Long id) {

        //根据id查询文章
        Article article = articleMapper.getById(id);

        //如果文章不存在，抛出异常
        if(article == null){
            throw new ArticleException(ArticleConstant.ARTICLE_NOT_FOUND);
        }

        Long like = article.getLikeCount();
        like += 1;
        article.setLikeCount(like);

        //修改文章表
        articleMapper.updateArticleLike(article);
    }

    /**
     * 取消点赞
     */
    @Override
    public void dislike(Long id) {
        //根据id查询文章
        Article article = articleMapper.getById(id);

        //如果文章不存在，抛出异常
        if(article == null){
            throw new ArticleException(ArticleConstant.ARTICLE_NOT_FOUND);
        }
        if(article.getLikeCount() == 0) {
            throw new ArticleException(ArticleConstant.ARTICLE_NOT_LIKE);
        }

        long like = article.getLikeCount();
        like -= 1;
        article.setLikeCount(like);

        //修改文章表
        articleMapper.updateArticleLike(article);
    }


    /**
     * 文章浏览数加一
     * 先更新数据库，再跟新缓存
     */
    @Override
    @Transactional
    public Long view(Long id) {
        //数据库文章浏览量先加1
        Article article = articleMapper.getById(id);
        if (article == null) {
            log.warn("文章不存在，id: {}", id);
            throw new ArticleException(ArticleConstant.ARTICLE_NOT_FOUND);
        }
        long newCount = article.getViewCount() + 1;
        article.setViewCount(newCount);
        //更新数据库
        articleMapper.updateArticleViewCount(newCount, id);

        // 通过Zset原子累加分数，不存在会自动创建
        incrementArticleScore(id, 1L);

        return newCount;
    }

    /**
     * 根据标签id分页查询该标签文章
     */
    @Override
    public PageResult pageByTagId(ArticlePageDTO articlePageDTO) {
        //根据标签查询文章id
        List<Long> articleIds = articleTagMapper.getArticleIdsByTagId(articlePageDTO.getTagId());
        if(articleIds != null && !articleIds.isEmpty()){
            //分页查询文章
            PageHelper.startPage(articlePageDTO.getPage(),articlePageDTO.getPageSize());
            Page<ArticleVO> page = articleMapper.pageByArticleIds(articleIds);
            return new PageResult(page.getTotal(),page.getResult());
        }else{
            return new PageResult(0L,null);
        }
    }


    // 更新/新增文章浏览量
    private void incrementArticleScore(Long id, long inc) {
        redisTemplate.opsForZSet().incrementScore(ArticleConstant.ARTICLE_RANK_KEY, id, inc);
    }

    //取浏览量前N名
    private Set<Long> topNFromRank(int n) {
        // 0 到 n-1 名，倒序
        Set<Object> set = redisTemplate.opsForZSet().reverseRange(ArticleConstant.ARTICLE_RANK_KEY, 0, n - 1);
        if (set == null || set.isEmpty()) return Set.of();
        return set.stream().map(obj -> (Long) obj).collect(Collectors.toSet());
    }

    /**
     * 获取文章浏览量排名前10的文章
     */
    @Override
    public List<ArticleVO> topArticle() {
        //从 ZSET 取前10名文章id
        Set<Long> topIds = topNFromRank(10);
        if (topIds.isEmpty()) {
            return List.of();
        }

        // 从 ZSET 拿到有序 id
        List<Long> order = new ArrayList<>(topIds);

        // 查询数据库
        List<ArticleVO> dbList = articleMapper.listByIds(order);

        // 转成Map，可通过文章id拿到实体类
        Map<Long, ArticleVO> map = dbList.stream()
                .collect(Collectors.toMap(ArticleVO::getId, Function.identity()));

        // 4. 按 ZSET 顺序组装结果
        return order.stream()
                .map(map::get)
                .filter(Objects::nonNull)   // 兜底 null
                .collect(Collectors.toList());
    }

    @Override
    public List<String> findAllArticleByCategory(String category) {
        return articleMapper.findAllArticleByCategory(category);
    }

    /**
     * 根据文章名称查找文章内容
     * @param articleName 文章名称
     */
    @Override
    public String findArticleContent(String articleName) {
        return articleMapper.findArticleContent(articleName);
    }

    /**
     * 根据文章名称查找文章标签
     * @param articleName 文章名称
     */
    @Override
    public List<String> findArticleTag(String articleName) {
        return articleMapper.findArticleTag(articleName);
    }

    /**
     * 根据文章名称查找文章分类
     * @param articleName 文章名称
     */
    @Override
    public String findArticleCategory(String articleName) {
        return articleMapper.findArticleCategory(articleName);
    }
}
